﻿Imports System.Text

Public Class dhtmlxGantt2
    Shared Property Task_Height As String = 16
    Shared Property Grid_Width As String = ""
    Shared Property Add_Column As Boolean = False
    Shared Property Columns As List(Of Column)
    Shared Property Tasks As List(Of Task)
    Shared Property SubScales As List(Of SubScale)
    Shared Property Links As List(Of Link)
    Shared Quote As String = Chr(34).ToString
    Shared sb As StringBuilder
    Shared Property Sortable As Boolean = False
    Shared Property AutoFit As Boolean = False
    Shared Property Api_Date As String = "%d/%m/%Y"
    Shared Property Date_Grid As String = "%d/%m/%Y"
    Shared Property Scale_Unit As scaleUnitEnum = scaleUnitEnum.day
    Shared Property Details_On_Create As Boolean = False
    Shared Property Details_On_Dblclick As Boolean = True
    Shared Property Drag_Links As Boolean = True
    Shared Property Drag_LightBox As Boolean = True
    Shared Property Drag_Move As Boolean = True
    Shared Property Drag_Resize As Boolean = True
    Shared Property Initial_Scroll As Boolean = True
    Shared Property Read_Only As Boolean = False
    Shared Property Select_Task As Boolean = True
    Shared Property Show_Progress As Boolean = False
    Shared Property Start_On_Monday As Boolean = True
    Shared Property stepBy As Integer = 1
    Shared Property Date_Scale As String = "%d %M"
    Shared Property HighlightWeekEnds As Boolean = true
    Shared Property Title As String = ""
    Shared Property Width As String = "100%"
    Shared Property Height As String = "100%"
    Shared Property Scale_Height As String = ""
    Shared Property Theme As skinEnum = skinEnum.normal
    Shared Property ClickAbleLinks As Boolean = False
    Shared Property ShowQuickInfo As Boolean = False
    Shared Property ShowToolTips As Boolean = False
    Shared Property Min_Column_Width As String = "20"
    Shared Property ColorCodeLinks As Boolean = False
    Shared Property ShowProgressText As Boolean = False
    Shared Property Row_Height As String = 20
    Shared Property link_line_width As Integer = 2
    Shared Property link_arrow_size As Integer = 6
    Shared Property link_wrapper_width As Integer = 20
    Shared Property round_dnd_dates As Boolean = False
    Shared Property start_date As String = ""
    Shared Property end_date As String = ""
    Shared Property HideBarText As Boolean = False
    Shared Property ShowToday As Boolean = True
    Shared Property LeftSidePattern As List(Of String)
    Shared Property RightSidePattern As List(Of String)
    Shared Property BarTextPattern As List(Of String)

    ''' <summary>
    ''' Set the scale width of the scale
    ''' </summary>
    ''' <param name="NumberOfScales">How many scales have been defined</param>
    ''' <param name="WidthOfEach">Width of each scale</param>
    ''' <remarks></remarks>
    Shared Sub SetScaleHeight(NumberOfScales As Integer, WidthOfEach As Integer)
        Dim sb As New StringBuilder
        sb.Append(NumberOfScales.ToString).Append("*").Append(WidthOfEach.ToString)
        Scale_Height = sb.ToString
    End Sub

    ''' <summary>
    ''' Enumeration for Priority
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum PriorityEnum
        [High]
        [Medium]
        [Low]
    End Enum

    ''' <summary>
    ''' PriorityEnum helper to return values
    ''' </summary>
    ''' <param name="pe">PriorityEnum parameter</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function PriorityHelper(pe As PriorityEnum) As String
        Select Case pe
            Case PriorityEnum.High : Return "1"
            Case PriorityEnum.Low : Return "3"
            Case PriorityEnum.Medium : Return "2"
        End Select
    End Function

    ''' <summary>
    ''' LightBox class is for building and rendering the lightbox for data entry
    ''' The current configuration meets the current columns defined
    ''' </summary>
    ''' <remarks></remarks>
    Public Class LightBox
        Shared Property LBColumns As List(Of LBColumn)
        Shared Property LBOptions As List(Of LBOption)
        Shared Property LBTime As List(Of String)

        ''' <summary>
        ''' Enumeration for the type of field to add to the lightbox
        ''' </summary>
        ''' <remarks></remarks>
        Public Enum FldTypeEnum
            [textarea]
            [select]
            [time]
            [template]
            [duration]
        End Enum

        ''' <summary>
        ''' Initialize the list of fields to add to the lightbox.
        ''' This method should be called first
        ''' </summary>
        ''' <remarks></remarks>
        Shared Sub Initialize()
            LBColumns = New List(Of LBColumn)
        End Sub



        ''' <summary>
        ''' Add a time format entry based on the DateFormatEnum for a time field of the lightbox
        ''' This also initializes the list of time formats to display for each defined field
        ''' </summary>
        ''' <param name="tim">DateFormatEnum parameter</param>
        ''' <remarks></remarks>
        Shared Sub NewTime(tim As DateFormatEnum)
            LBTime = New List(Of String)
            LBTime.Add(DateHelper(tim))
        End Sub

        ''' <summary>
        ''' Add a time format entry based on the DateFormatEnum for a time field of the lightbox
        ''' </summary>
        ''' <param name="tim">DateFormatEnum parameter</param>
        ''' <remarks></remarks>
        Shared Sub AddTime(tim As DateFormatEnum)
            LBTime.Add(DateHelper(tim))
        End Sub

        ''' <summary>
        ''' Add an option Key,Label entry for a defined options field of the lightbox
        ''' This also initializes the a new option list for a defined field
        ''' </summary>
        ''' <param name="key">Key of the option</param>
        ''' <param name="label">Label of the option</param>
        ''' <remarks></remarks>
        Shared Sub NewOption(key As String, label As String)
            LBOptions = New List(Of LBOption)
            AddOption(key, label)
        End Sub

        ''' <summary>
        ''' Add an option Key,Label entry for a defined options field of the lightbox
        ''' </summary>
        ''' <param name="key">Key of the option</param>
        ''' <param name="label">Label of the option</param>
        ''' <remarks></remarks>
        Shared Sub AddOption(key As String, label As String)
            Dim nopt As New LBOption
            nopt.key = key
            nopt.label = label
            LBOptions.Add(nopt)
        End Sub

        ''' <summary>
        ''' Generates the time format string
        ''' </summary>
        ''' <returns>Time format string</returns>
        ''' <remarks></remarks>
        Shared Function TimeBuilder() As String
            Dim i As Integer = 0
            Dim sb As New StringBuilder
            sb.Append("[")
            For Each timeX As String In LBTime
                sb.Append(Quote).Append(timeX).Append(Quote)
                i = i + 1
                If i < LBTime.Count Then sb.Append(", ")
            Next
            sb.Append("]")
            Return sb.ToString
        End Function

        ''' <summary>
        ''' Generates the options string
        ''' </summary>
        ''' <returns>Options string</returns>
        ''' <remarks></remarks>
        Shared Function OptionsBuilder() As String
            Dim i As Integer = 0
            Dim sb As New StringBuilder
            sb.Append("[")
            For Each optX As LBOption In LBOptions
                sb.Append(optX.ToString)
                i = i + 1
                If i < LBOptions.Count Then sb.Append(", ")
            Next
            sb.Append("]")
            Return sb.ToString
        End Function

        ''' <summary>
        ''' LBOption holds the definition of an option, Key, Label dictionary
        ''' </summary>
        ''' <remarks></remarks>
        Public Class LBOption
            Property key As String = ""
            Property label As String = ""

            Sub New()
                MyBase.new()
            End Sub

            ''' <summary>
            ''' Generates the options string for each option
            ''' </summary>
            ''' <returns>JSON Option string</returns>
            ''' <remarks></remarks>
            Public Overrides Function ToString() As String
                Dim sb As New StringBuilder
                sb.Append("{")
                Dim pb As New PropertyBuilder
                pb.AddProperty("key", pb.InQuotes(key))
                pb.AddProperty("label", pb.InQuotes(label))
                sb.Append(pb.ToJSON)
                sb.Append("}")
                Return sb.ToString
            End Function

        End Class

        ''' <summary>
        ''' LBColumn class for the definition of a LightBox column/field
        ''' </summary>
        ''' <remarks></remarks>
        Public Class LBColumn
            Property name As String = ""
            Property height As Integer = 0
            Property mapTo As String = ""
            Property type As FldTypeEnum
            Property focus As Boolean
            Property timeformat As String = ""
            Property options As String = ""

            Sub New()
                MyBase.new()
            End Sub

            ''' <summary>
            ''' Generates the string definition of a column in a lightbox
            ''' </summary>
            ''' <returns>JSON lightbox column definition</returns>
            ''' <remarks></remarks>
            Public Overrides Function ToString() As String
                Dim sb As New StringBuilder
                Dim pb As New PropertyBuilder
                sb.Append("{")
                pb.AddProperty("name", pb.InQuotes(name))
                pb.AddProperty("height", height.ToString)
                pb.AddProperty("type", pb.InQuotes(type.ToString))
                pb.AddProperty("map_to", pb.InQuotes(mapTo))
                Select Case type
                    Case FldTypeEnum.select
                        pb.AddProperty("options", options)
                    Case FldTypeEnum.textarea
                        pb.AddProperty("focus", focus.ToString.ToLower)
                    Case FldTypeEnum.time
                        pb.AddProperty("time_format", timeformat)
                End Select
                sb.Append(pb.ToJSON)
                sb.Append("}")
                Return sb.ToString
            End Function
        End Class

        ''' <summary>
        ''' Add a column/field to a lightbox for data entry
        ''' </summary>
        ''' <param name="name">Name of the column</param>
        ''' <param name="height">Height of the column</param>
        ''' <param name="mapTo">Field mapping to column</param>
        ''' <param name="type">Type of field this is</param>
        ''' <param name="focus">Receives focus</param>
        ''' <param name="options">Options for a select column</param>
        ''' <param name="timeformat">Time format for a duration/time column</param>
        ''' <remarks></remarks>
        Public Shared Sub AddColumn(name As String, height As Integer, mapTo As String, type As FldTypeEnum, focus As Boolean, options As String, timeformat As String)
            Dim nFld As New LBColumn
            With nFld
                .name = name
                .height = height
                .mapTo = mapTo
                .type = type
                .focus = focus
                .options = options
                .timeformat = timeformat
            End With
            LBColumns.Add(nFld)
        End Sub

        ''' <summary>
        ''' Generates the lightbox definition based on the defined columns including a "Complete" button that
        ''' links to the % Complete column
        ''' </summary>
        ''' <returns>Lightbox definition</returns>
        ''' <remarks></remarks>
        Public Shared Function Render() As String
            Dim i As Integer = 0
            Dim sb As New StringBuilder
            sb.Append("gantt.locale.labels[").Append(Quote).Append("section_priority").Append(Quote).Append("] = ").Append(Quote).Append("Priority").Append(Quote).AppendLine(";")            sb.Append("gantt.locale.labels[").Append(Quote).Append("section_predecessors").Append(Quote).Append("] = ").Append(Quote).Append("Predecessors").Append(Quote).AppendLine(";")            sb.Append("gantt.locale.labels[").Append(Quote).Append("section_user").Append(Quote).Append("] = ").Append(Quote).Append("Resources").Append(Quote).AppendLine(";")            sb.Append("gantt.locale.labels[").Append(Quote).Append("section_progress").Append(Quote).Append("] = ").Append(Quote).Append("% Complete").Append(Quote).AppendLine(";")            sb.Append("gantt.locale.labels[").Append(Quote).Append("section_description").Append(Quote).Append("] = ").Append(Quote).Append("Task Name").Append(Quote).AppendLine(";")            sb.Append("gantt.locale.labels[").Append(Quote).Append("section_time").Append(Quote).Append("] = ").Append(Quote).Append("Start - Finish").Append(Quote).AppendLine(";")            sb.Append("gantt.locale.labels[").Append(Quote).Append("complete_button").Append(Quote).Append("] = ").Append(Quote).Append("Complete").Append(Quote).AppendLine(";")
            Dim sbx As New StringBuilder
            sbx.Append(Quote).Append("dhx_save_btn").Append(Quote).Append(",")
            sbx.Append(Quote).Append("dhx_cancel_btn").Append(Quote).Append(",")
            sbx.Append(Quote).Append("complete_button").Append(Quote)
            sb.Append("gantt.config.buttons_left=[").Append(sbx.ToString).AppendLine("];")            sb.AppendLine("gantt.config.lightbox.sections = [")
            For Each lbc As LBColumn In LBColumns
                sb.Append(lbc.ToString)
                i = i + 1
                If i < LBColumns.Count Then sb.AppendLine(", ")
            Next
            sb.AppendLine("];")
            Return sb.ToString
        End Function
    End Class

    ''' <summary>
    ''' Enumeration for DateFormat
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum DateFormatEnum
        [d]
        [dd]
        [ddd]
        [dddd]
        [week]
        [m]
        [mm]
        [mmm]
        [mmmm]
        [yy]
        [yyyy]
        [hours]
        [hours24]
        [minutes]
        [seconds]
        [smallam]
        [bigam]
        [comma]
        [blank]
        [semicolon]
        [time]
    End Enum

    ''' <summary>
    ''' Return a string value of a DateFormatEnum value
    ''' </summary>
    ''' <param name="df">DateFormatEnum parameter</param>
    ''' <returns>String value of DateFormatEnum parameter</returns>
    ''' <remarks></remarks>
    Public Shared Function DateHelper(df As DateFormatEnum) As String
        Select Case df
            Case DateFormatEnum.d : Return "%j"
            Case DateFormatEnum.dd : Return "%d"
            Case DateFormatEnum.ddd : Return "%D"
            Case DateFormatEnum.dddd : Return "%l"
            Case DateFormatEnum.week : Return "%W"
            Case DateFormatEnum.m : Return "%m"
            Case DateFormatEnum.mm : Return "%n"
            Case DateFormatEnum.mmm : Return "%M"
            Case DateFormatEnum.mmmm : Return "%F"
            Case DateFormatEnum.yy : Return "%y"
            Case DateFormatEnum.yyyy : Return "%Y"
            Case DateFormatEnum.hours : Return "%h"
            Case DateFormatEnum.hours24 : Return "%H"
            Case DateFormatEnum.minutes : Return "%i"
            Case DateFormatEnum.seconds : Return "%s"
            Case DateFormatEnum.smallam : Return "%a"
            Case DateFormatEnum.bigam : Return "%A"
            Case DateFormatEnum.comma : Return ", "
            Case DateFormatEnum.blank : Return " "
            Case DateFormatEnum.semicolon : Return ":"
            Case DateFormatEnum.time : Return "%H:%i"
        End Select
    End Function

    ''' <summary>
    ''' Add a pattern for the text to display inside the bar of a task.
    ''' By default the task name is displayed, however you can define patterns of your own.
    ''' </summary>
    ''' <param name="prefix">Text that appears before a task linked field</param>
    ''' <param name="ptn">The task field to display</param>
    ''' <param name="suffix">Text to display after the task linked field</param>
    ''' <remarks></remarks>
    Shared Sub AddBarTextPattern(ByVal prefix As String, ByVal ptn As PatternEnum, ByVal suffix As String)
        Dim sb As New StringBuilder
        If Len(prefix) > 0 Then sb.Append(Quote).Append(prefix).Append(Quote).Append(" + ")
        sb.Append("task.").Append(ptn.ToString)
        If Len(suffix) > 0 Then sb.Append(" + ").Append(Quote).Append(suffix).Append(Quote)
        BarTextPattern.Add(sb.ToString)
    End Sub

    ''' <summary>
    ''' Add a pattern for the text to display on the right side of the task bar.
    ''' </summary>
    ''' <param name="prefix">Text that appears before a task linked field</param>
    ''' <param name="ptn">The task field to display</param>
    ''' <param name="suffix">Text to display after the task linked field</param>
    ''' <remarks></remarks>
    Shared Sub AddRightSidePattern(ByVal prefix As String, ByVal ptn As PatternEnum, ByVal suffix As String)
        Dim sb As New StringBuilder
        If Len(prefix) > 0 Then sb.Append(Quote).Append(prefix).Append(Quote).Append(" + ")
        sb.Append("task.").Append(ptn.ToString)
        If Len(suffix) > 0 Then sb.Append(" + ").Append(Quote).Append(suffix).Append(Quote)
        RightSidePattern.Add(sb.ToString)
    End Sub

    ''' <summary>
    ''' Add a pattern for the text to display on the left side of the task bar.
    ''' </summary>
    ''' <param name="prefix">Text that appears before a task linked field</param>
    ''' <param name="ptn">The task field to display</param>
    ''' <param name="suffix">Text to display after the task linked field</param>
    ''' <remarks></remarks>
    Shared Sub AddLeftSidePattern(ByVal prefix As String, ByVal ptn As PatternEnum, ByVal suffix As String)
        Dim sb As New StringBuilder
        If Len(prefix) > 0 Then sb.Append(Quote).Append(prefix).Append(Quote).Append(" + ")
        sb.Append("task.").Append(ptn.ToString)
        If Len(suffix) > 0 Then sb.Append(" + ").Append(Quote).Append(suffix).Append(Quote)
        LeftSidePattern.Add(sb.ToString)
    End Sub

    ''' <summary>
    ''' Method to define scaleformats quickly
    ''' </summary>
    ''' <param name="Delim">The delimiter to separate the scales with</param>
    ''' <param name="eachFormat">Numerous DateFormatEnum parameters for the scale</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function ScaleFormat(ByVal Delim As String, ByVal ParamArray eachFormat As DateFormatEnum()) As String
        Dim sb As New StringBuilder
        For i As Integer = 0 To eachFormat.Length - 1
            sb.Append(DateHelper(eachFormat(i)))
            sb.Append(Delim)
        Next
        Return sb.ToString.Trim
    End Function

    ''' <summary>
    ''' Return a date value of dd/MM/yyyy format as new Date string
    ''' </summary>
    ''' <param name="dValue">Date value in dd/MM/yyyy format</param>
    ''' <param name="MonthStatus">Use '+ 1' as MonthStatus to add a month to the output </param>
    ''' <returns>New Date</returns>
    ''' <remarks></remarks>
    Private Shared Function MakeDate(dValue As String, MonthStatus As Integer) As String
        ' convert dd/MM/yyyy format to date value for the google 
        ' chart to interpret
        Dim intY As String = Split(dValue, "/")(2)
        Dim intM As String = Split(dValue, "/")(1)
        Dim intD As String = Split(dValue, "/")(0)
        Return "new Date(" & Val(intY) & ", " & Val(intM) + MonthStatus & ", " & Val(intD) & ")"
    End Function

    ''' <summary>
    ''' Class for building the property values
    ''' </summary>
    ''' <remarks></remarks>
    Public Class PropertyBuilder
        Private Properties As Dictionary(Of Object, Object)
        Private sb As StringBuilder
        Private Quote As String = Chr(34).ToString

        Sub New()
            MyBase.new()
            sb = New StringBuilder
            Properties = New Dictionary(Of Object, Object)
        End Sub

        ''' <summary>
        ''' Initialize the property builder class
        ''' </summary>
        ''' <remarks></remarks>
        Sub Initialize()
            sb = New StringBuilder
            Properties = New Dictionary(Of Object, Object)
        End Sub

        ''' <summary>
        ''' Add a property defintion
        ''' </summary>
        ''' <param name="sKey">Property Name</param>
        ''' <param name="sValue">Property Value</param>
        ''' <remarks></remarks>
        Public Sub AddProperty(sKey As Object, sValue As Object)
            If Properties.ContainsKey(sKey) = True Then
                Properties.Item(sKey) = sValue
            Else
                Properties.Add(sKey, sValue)
            End If
        End Sub

        ''' <summary>
        ''' Generates the propery Definition from all added properties
        ''' </summary>
        ''' <returns>Property String Definition</returns>
        ''' <remarks></remarks>
        Public Function Render() As String
            sb = New StringBuilder
            Dim rsCnt As Integer = 0
            For Each pair As KeyValuePair(Of Object, Object) In Properties
                rsCnt = rsCnt + 1
                sb.Append(pair.Key.ToString).Append("=").Append(Quote).Append(pair.Value.ToString).Append(Quote)
                If rsCnt <> Properties.Count Then sb.Append(" ")
            Next
            Return sb.ToString
        End Function

        ''' <summary>
        ''' Generates the property definition from all added properties in JSON format
        ''' </summary>
        ''' <returns>JSON property definition</returns>
        ''' <remarks></remarks>
        Public Function ToJSON() As String
            sb = New StringBuilder
            Dim rsCnt As Integer = 0
            For Each pair As KeyValuePair(Of Object, Object) In Properties
                rsCnt = rsCnt + 1
                sb.Append(pair.Key.ToString).Append(":").Append(pair.Value.ToString)
                If rsCnt <> Properties.Count Then sb.Append(",")
            Next
            Return sb.ToString
        End Function

        ''' <summary>
        ''' Append a string to the stringbuilder, this is not used for property building
        ''' </summary>
        ''' <param name="sLine">String to append</param>
        ''' <remarks></remarks>
        Public Sub AppendLine(sLine As String)
            sb.AppendLine(sLine)
        End Sub

        ''' <summary>
        ''' Append a string to the stringbuilder, this is not used for property building
        ''' </summary>
        ''' <param name="sLine">String to append</param>
        ''' <remarks></remarks>
        Public Sub Append(sLine As String)
            sb.Append(sLine)
        End Sub

        ''' <summary>
        ''' Return a double quoted string
        ''' </summary>
        ''' <param name="value">String to double quote</param>
        ''' <returns>Double quoted string</returns>
        ''' <remarks></remarks>
        Public Function InQuotes(value As String) As String
            Return Quote & value & Quote
        End Function

        ''' <summary>
        ''' Return a single quoted string
        ''' </summary>
        ''' <param name="value">String to be single quoted</param>
        ''' <returns>Single quoted string</returns>
        ''' <remarks></remarks>
        Public Function InSingleQuotes(value As String) As String
            Return "'" & value & "'"
        End Function

        ''' <summary>
        ''' Return all values added to the stringbuilder, this excludes defined properties
        ''' </summary>
        ''' <returns>String</returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Return sb.ToString
        End Function
    End Class

    ''' <summary>
    ''' Set the scale of the gantt chart at first level
    ''' </summary>
    ''' <param name="sScale_Unit">The unit to use</param>
    ''' <param name="sstepBy">Stepping of the scale</param>
    ''' <param name="sDate_Scale">The date format of the scale</param>
    ''' <remarks></remarks>
    Public Shared Sub SetScale(sScale_Unit As scaleUnitEnum, Optional sstepBy As Integer = 1, Optional sDate_Scale As String = "%Y")
        Scale_Unit = sScale_Unit
        stepBy = sstepBy
        Date_Scale = sDate_Scale
    End Sub

    ''' <summary>
    ''' Define how resources field will be returned in the grid
    ''' If there is no resource return a blank field. This is used in the column definition of the resources field
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TemplateResources() As String
        Dim sb As New StringBuilder
        sb.Append("function(item) {")
        sb.Append("if (!item.users) return ").Append(QuoteString("")).AppendLine(";")
        sb.Append("return item.users;").Append("}")
        Return sb.ToString
    End Function

    ''' <summary>
    ''' Define how start date will be returned in the grid
    ''' This is used in the column definition of the start date column
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TemplateStartDate() As String
        Dim sb As New StringBuilder
        sb.Append("function(item) {")
        sb.Append("if (!item.start_date) return ").Append(QuoteString("")).AppendLine(";")
        sb.Append("return item.start_date;").Append("}")
        Return sb.ToString
    End Function

    ''' <summary>
    ''' Define how finish date will be returned in the grid
    ''' This is used in the column definition of the start date column
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TemplateFinishDate() As String
        Dim sb As New StringBuilder
        sb.Append("function(item) {")
        sb.Append("if (!item.end_date) return ").Append(QuoteString("")).AppendLine(";")
        sb.Append("return item.end_date;").Append("}")
        Return sb.ToString
    End Function

    ''' <summary>
    ''' Define how duration will be returned in the grid
    ''' This is used in the column definition of the start date column
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TemplateDuration() As String
        Dim sb As New StringBuilder
        sb.Append("function(item) {")
        sb.Append("if (!item.duration) return ").Append(QuoteString("")).AppendLine(";")
        sb.Append("return item.duration + ").Append(Quote).Append(" days").Append(Quote).Append(";}")
        Return sb.ToString
    End Function

    ''' <summary>
    ''' Define how predecessors will be returned in the grid
    ''' This is used in the column definition of the start date column
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TemplatePredecessors() As String
        Dim sb As New StringBuilder
        sb.Append("function(item) {")
        sb.Append("if (!item.predecessors) return ").Append(QuoteString("")).AppendLine(";")
        sb.Append("return item.predecessors;").Append("}")
        Return sb.ToString
    End Function

    ''' <summary>
    ''' Define how the percentage will be returned in the grid
    ''' This is used in the column definition of the start date column
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TemplatePercentage() As String
        Dim sb As New StringBuilder
        sb.Append("function(item) {")
        sb.Append("return item.progress*100 + ")
        sb.Append(Quote).Append("%").Append(Quote).Append(";").Append("}")
        Return sb.ToString
    End Function

    ''' <summary>
    ''' Enumeration for patterns based on the defined columns of the gantt
    ''' For usage when defining left side text, bar text and right side text
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum PatternEnum
        [id]
        [text]
        [start_date]
        [end_date]
        [duration]
        [progress]
        [users]
        [predecessors]
        [priority]
    End Enum

    ''' <summary>
    ''' Initialize the gantt chart options. Run this method first but then you can initialize
    ''' </summary>
    Public Shared Sub Initialize()
        ' compulsory initialization
        BarTextPattern = New List(Of String)
        LeftSidePattern = New List(Of String)
        RightSidePattern = New List(Of String)
        sb = New StringBuilder
        Columns = New List(Of Column)
        Tasks = New List(Of Task)
        SubScales = New List(Of SubScale)
        Links = New List(Of Link)
        ' you can define your own columns
        AddColumn("id", "ID", False, alignEnum.left, "", "40")
        AddColumn("text", "Task Name", True, alignEnum.left, "", "200")
        AddColumn("priority", "Priority", False, alignEnum.center, "myPriority", "50")
        AddColumn("start_date", "Start", False, alignEnum.left, TemplateStartDate, "70")
        AddColumn("end_date", "Finish", False, alignEnum.left, TemplateFinishDate, "70")
        AddColumn("duration", "Duration", False, alignEnum.center, TemplateDuration, "60")
        AddColumn("progress", "% Complete", False, alignEnum.center, TemplatePercentage, "70")
        AddColumn("predecessors", "Predecessors", False, alignEnum.left, TemplatePredecessors, "80")
        AddColumn("users", "Resources", False, alignEnum.left, TemplateResources, "80")
        Grid_Width = "700"
        ShowProgressText = False
        Add_Column = False
        Sortable = False
        Row_Height = 20
        Task_Height = 16
        HighlightWeekEnds = True
        Read_Only = True
        Start_On_Monday = False
        Show_Progress = True
        ShowQuickInfo = False
        ShowToolTips = True
        Theme = skinEnum.skyblue
        ColorCodeLinks = True
        ClickAbleLinks = True
        link_line_width = 2
        HideBarText = False
        LightBox.Initialize()
        ' build the lightbox
        LightBox.NewOption("0", "0%")
        LightBox.AddOption("0.1", "10%")
        LightBox.AddOption("0.15", "15%")
        LightBox.AddOption("0.2", "20%")
        LightBox.AddOption("0.25", "25%")
        LightBox.AddOption("0.3", "30%")
        LightBox.AddOption("0.35", "35%")
        LightBox.AddOption("0.4", "40%")
        LightBox.AddOption("0.45", "45%")
        LightBox.AddOption("0.5", "50%")
        LightBox.AddOption("0.55", "55%")
        LightBox.AddOption("0.6", "60%")
        LightBox.AddOption("0.65", "65%")
        LightBox.AddOption("0.7", "70%")
        LightBox.AddOption("0.75", "75%")
        LightBox.AddOption("0.8", "80%")
        LightBox.AddOption("0.85", "85%")
        LightBox.AddOption("0.9", "90%")
        LightBox.AddOption("0.95", "95%")
        LightBox.AddOption("1", "100%")
        Dim pOptions As String = LightBox.OptionsBuilder
        ' priority
        LightBox.NewOption("1", "High")
        LightBox.AddOption("2", "Medium")
        LightBox.AddOption("3", "Low")
        Dim px As String = LightBox.OptionsBuilder
        LightBox.NewTime(DateFormatEnum.dd)
        LightBox.AddTime(DateFormatEnum.m)
        LightBox.AddTime(DateFormatEnum.yyyy)
        Dim pTime As String = LightBox.TimeBuilder
        LightBox.AddColumn("description", 38, "text", LightBox.FldTypeEnum.textarea, True, "", "")
        LightBox.AddColumn("priority", 22, "priority", LightBox.FldTypeEnum.select, False, px, "")
        LightBox.AddColumn("time", 72, "auto", LightBox.FldTypeEnum.duration, False, "", pTime)
        LightBox.AddColumn("progress", 22, "progress", LightBox.FldTypeEnum.select, False, pOptions, "")
        LightBox.AddColumn("predecessors", 38, "predecessors", LightBox.FldTypeEnum.textarea, False, "", "")
        LightBox.AddColumn("user", 38, "users", LightBox.FldTypeEnum.textarea, False, "", "")
    End Sub

    ''' <summary>
    ''' Add a dependency for a task using fs, ss and ff as part of the definitions
    ''' </summary>
    ''' <param name="source">Source Task ID</param>
    ''' <param name="target">Target task ID</param>
    ''' <remarks></remarks>
    Public Shared Sub AddLink(source As String, target As String)
        Dim ltype As LinkTypeEnum = LinkTypeEnum.finish_to_start
        source = source.Replace(",", ";")
        target = target.Replace(",", ";")
        target = Split(target, ";")(0)
        If InStr(1, source, "fs", CompareMethod.Text) > 0 Then ltype = LinkTypeEnum.finish_to_start
        If InStr(1, source, "ss", CompareMethod.Text) > 0 Then ltype = LinkTypeEnum.start_to_start
        If InStr(1, source, "ff", CompareMethod.Text) > 0 Then ltype = LinkTypeEnum.finish_to_finish
        source = source.Replace("fs", "").Replace("ss", "").Replace("ff", "")
        target = target.Replace("fs", "").Replace("ss", "").Replace("ff", "")
        AddLink(source, target, ltype)
    End Sub

    ''' <summary>
    ''' Add a dependency for a task
    ''' </summary>
    ''' <param name="source">Source Task ID</param>
    ''' <param name="target">Target Task ID</param>
    ''' <param name="type">Link Type</param>
    ''' <remarks></remarks>
    Public Shared Sub AddLink(ByVal source As String, ByVal target As String, type As LinkTypeEnum)
        Dim spSource() As String
        source = source.Replace(",", ";")
        target = target.Replace(",", ";")
        spSource = Split(source, ";")
        target = Split(target, ";")(0)
        If InStr(1, source, "fs", CompareMethod.Text) > 0 Then type = LinkTypeEnum.finish_to_start
        If InStr(1, source, "ss", CompareMethod.Text) > 0 Then type = LinkTypeEnum.start_to_start
        If InStr(1, source, "ff", CompareMethod.Text) > 0 Then type = LinkTypeEnum.finish_to_finish
        source = source.Replace("fs", "").Replace("ss", "").Replace("ff", "")
        target = target.Replace("fs", "").Replace("ss", "").Replace("ff", "")
        For Each strSource As String In spSource
            Dim nLink As New Link
            With nLink
                .source = strSource
                .target = target
                .type = type
            End With
            Links.Add(nLink)
        Next
    End Sub

    ''' <summary>
    ''' Add a task to the gantt
    ''' </summary>
    ''' <param name="id">Task ID number</param>
    ''' <param name="text">Task Name</param>
    ''' <param name="start_date">Start Date</param>
    ''' <param name="end_date">Finish Date</param>
    ''' <param name="duration">Duration</param>
    ''' <param name="progress">Progress 0 - 1</param>
    ''' <param name="parent">Task Parent ID</param>
    ''' <param name="open">Task Open or Not</param>
    ''' <param name="order">Owner Defined</param>
    ''' <param name="users">Resources</param>
    ''' <param name="predecessors">Predecessors</param>
    ''' <remarks></remarks>
    Public Shared Sub AddTask(id As String, text As String, start_date As String, end_date As String, duration As String, progress As String, parent As String, open As Boolean, order As String, users As String, predecessors As String, priority As String)
        Dim nTask As New dhtmlxGantt2.Task
        With nTask
            .id = id
            .parent = parent
            If .parent = "0" Then .parent = ""
            .wbs = ""
            .priority = priority
            If .priority = "" Then .priority = "3"
            .text = text
            .duration = duration.Replace("d", "")
            If .duration = "0" Then .duration = ""
            .start_date = start_date
            .end_date = end_date
            .progress = progress
            .predecessors = predecessors
            .users = users
            .order = order
            .open = open
            ' fix unavailable dates
            If Len(.start_date) = 0 And Len(.end_date) = 0 Then
                .start_date = Strings.Format(Today.Date, "dd/MM/yyyy")
                .end_date = .start_date
            End If
            If Len(.start_date) = 0 And Len(.end_date) > 0 Then .start_date = .end_date
            If Len(.end_date) = 0 And Len(.start_date) > 0 Then .end_date = .start_date
            If Len(.duration) = 0 Then .duration = 1
        End With
        If Len(nTask.text) > 0 And Len(nTask.duration) > 0 Then Tasks.Add(nTask)
    End Sub

    ''' <summary>
    ''' Enumeration for skins that are usable
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum skinEnum
        [normal]
        [broadway]
        [meadow]
        [skyblue]
        [terrance]
    End Enum

    ''' <summary>
    ''' Enumeration for link types
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum LinkTypeEnum
        [start_to_start] = 1
        [finish_to_start] = 0
        [finish_to_finish] = 2
    End Enum

    ''' <summary>
    ''' Link Type Helper to return link type number
    ''' </summary>
    ''' <param name="type">LinkTypeEnum parameter</param>
    ''' <returns>Value id of link</returns>
    ''' <remarks></remarks>
    Public Shared Function LinkTypeHelper(type As LinkTypeEnum) As String
        Select Case type
            Case LinkTypeEnum.finish_to_finish : Return "2"
            Case LinkTypeEnum.start_to_start : Return "1"
            Case LinkTypeEnum.finish_to_start : Return "0"
        End Select
    End Function

    ''' <summary>
    ''' Return a double quoted string
    ''' </summary>
    ''' <param name="value">String to double quote</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function QuoteString(value As String) As String
        Return Quote & value & Quote
    End Function

    ''' <summary>
    ''' Link Class
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Link
        Property source As String = ""
        Property target As String = ""
        Property type As LinkTypeEnum = LinkTypeEnum.finish_to_start

        Sub New()
            MyBase.new()
        End Sub

        ''' <summary>
        ''' New link definition
        ''' </summary>
        ''' <param name="source">Source Task ID</param>
        ''' <param name="target">Target Task ID</param>
        ''' <param name="type">LinkTypeEnum parameter</param>
        ''' <remarks></remarks>
        Sub New(source As String, target As String, type As LinkTypeEnum)
            MyBase.new()
            Me.source = source
            Me.target = target
            Me.type = type
        End Sub

        ''' <summary>
        ''' Return link definition string
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Dim sb As New StringBuilder
            sb.Append(", source:").Append(source).Append(", target:").Append(target).Append(", type:").Append(Quote)
            sb.Append(LinkTypeHelper(type)).Append(Quote).Append("}")
            Return sb.ToString
        End Function
    End Class

    ''' <summary>
    ''' Subscale class
    ''' </summary>
    ''' <remarks></remarks>
    Public Class SubScale
        Property unit As scaleUnitEnum
        Property stepBy As Integer = 1
        Property dateFormat As String = ""

        Sub New()
            MyBase.new()
        End Sub

        ''' <summary>
        ''' New sub scale definition
        ''' </summary>
        ''' <param name="unit"></param>
        ''' <param name="stepBy"></param>
        ''' <param name="dateFormat"></param>
        ''' <remarks></remarks>
        Sub New(unit As scaleUnitEnum, stepBy As Integer, dateFormat As String)
            MyBase.new()
            Me.unit = unit
            Me.stepBy = stepBy
            Me.dateFormat = dateFormat
        End Sub

        ''' <summary>
        ''' Returns sub scale definition string
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Dim sb As New StringBuilder
            sb.Append("{unit:").Append(QuoteString(unit.ToString)).Append(", step:").Append(stepBy.ToString).Append(", date:").Append(QuoteString(dateFormat)).Append("}")
            Return sb.ToString
        End Function
    End Class

    ''' <summary>
    ''' Enumeration for scale units
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum scaleUnitEnum
        [month]
        [minute]
        [hour]
        [day]
        [week]
        [year]
    End Enum

    ''' <summary>
    ''' Enumeration for alignment of grid columns
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum alignEnum
        [center]
        [left]
        [right]
    End Enum

    ''' <summary>
    ''' Task Class
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Task
        Property id As String = ""
        Property parent As String = ""
        Property wbs As String = ""
        Property text As String = ""
        Property duration As String = ""
        Property start_date As String = ""
        Property end_date As String = ""
        Property progress As String = ""
        Property predecessors As String = ""
        Property users As String = ""
        Property open As Boolean = False
        Property order As String = ""
        Property priority As String = ""

        Sub New()
            MyBase.new()
        End Sub

        ''' <summary>
        ''' New task definition
        ''' </summary>
        ''' <param name="id">Task ID</param>
        ''' <param name="parent">Parent Task ID</param>
        ''' <param name="wbs">Work Breakdown Structure</param>
        ''' <param name="text">Task Name</param>
        ''' <param name="duration">Duration</param>
        ''' <param name="start_date">Start Date</param>
        ''' <param name="end_date">Finish Date</param>
        ''' <param name="progress">Progress from 0-1</param>
        ''' <param name="predecessors">Predecessors</param>
        ''' <param name="users">Resources</param>
        ''' <param name="open">Open or Not</param>
        ''' <param name="order">User Defined</param>
        ''' <param name="priority">Priority</param>
        ''' <remarks></remarks>
        Sub New(id As String, parent As String, wbs As String, text As String, duration As String, start_date As String, end_date As String, progress As String, predecessors As String, users As String, open As Boolean, order As String, priority As String)
            Me.id = id
            Me.parent = parent
            Me.wbs = wbs
            Me.text = text
            Me.duration = duration
            Me.start_date = start_date
            Me.end_date = end_date
            Me.progress = progress
            Me.predecessors = predecessors
            Me.users = users
            Me.open = open
            Me.order = order
            Me.priority = priority
        End Sub

        ''' <summary>
        ''' Returns task definition JSON string
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Dim sb As New StringBuilder
            sb.Append("{")
            ' compulsory fields
            sb.Append("id:").Append(id).Append(",")
            sb.Append("text:").Append(QuoteString(text)).Append(",")
            sb.Append("duration:").Append(QuoteString(duration)).Append(",")
            sb.Append("start_date:").Append(QuoteString(start_date)).Append(",")
            sb.Append("progress:").Append(progress).Append(",")
            ' optional fields
            If parent = "0" Then parent = ""
            If Len(wbs) > 0 Then sb.Append("wbs:").Append(wbs).Append(",")
            If Len(parent) > 0 Then sb.Append("parent:").Append(parent).Append(",")
            If Len(end_date) > 0 Then sb.Append("end_date:").Append(QuoteString(end_date)).Append(",")
            If Len(predecessors) > 0 Then sb.Append("predecessors:").Append(predecessors).Append(",")
            If Len(users) > 0 Then sb.Append("users:").Append(QuoteString(users)).Append(",")
            If Len(order) > 0 Then sb.Append("order:").Append(order).Append(",")
            sb.Append("priority:").Append(QuoteString(priority)).Append(",")
            ' closure
            sb.Append("open:").Append(open.ToString.ToLower)
            sb.Append("}")
            Return sb.ToString
        End Function

    End Class

    ''' <summary>
    ''' Column Class for grid columns
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Column
        Property name As String = ""
        Property label As String = ""
        Property tree As Boolean = False
        Property align As alignEnum = alignEnum.left
        Property template As String = ""
        Property width As String = "*"

        Sub New()
            MyBase.new()
        End Sub

        ''' <summary>
        ''' Column definition
        ''' </summary>
        ''' <param name="name">Column Name</param>
        ''' <param name="label">Column Label</param>
        ''' <param name="tree">Is a tree structure</param>
        ''' <param name="align">Alignment of column</param>
        ''' <param name="template">Template for reading data from task structure</param>
        ''' <param name="width">Width of the column</param>
        ''' <remarks></remarks>
        Sub New(name As String, label As String, tree As Boolean, align As alignEnum, template As String, width As String)
            MyBase.new()
            Me.name = name
            Me.label = label
            Me.tree = tree
            Me.align = align
            Me.template = template
            Me.width = width
        End Sub

        ''' <summary>
        ''' Return column definition as JSON format
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Dim sb As New StringBuilder
            sb.Append("{")
            sb.Append("name:").Append(Quote).Append(name).Append(Quote).Append(", label:").Append(Quote).Append(label).Append(Quote)
            sb.Append(", tree:").Append(tree.ToString.ToLower).Append(", width:'").Append(width).Append("'")
            sb.Append(", align:").Append(Quote).Append(align.ToString).Append(Quote)
            If Len(template) > 0 Then sb.Append(", template:").Append(template)
            sb.Append("}")
            Return sb.ToString
        End Function
    End Class

    ''' <summary>
    ''' Add a subscale to the gantt chart
    ''' </summary>
    ''' <param name="unit">The unit of measurement</param>
    ''' <param name="stepBy">How many times to step</param>
    ''' <param name="dateFormat">What is the date format</param>
    ''' <remarks></remarks>
    Public Shared Sub AddSubScale(unit As scaleUnitEnum, stepBy As Integer, dateFormat As String)
        Dim nss As New SubScale
        With nss
            .unit = unit
            .stepBy = stepBy
            .dateFormat = dateFormat
        End With
        SubScales.Add(nss)
    End Sub

    ''' <summary>
    ''' Add a column to the grid
    ''' </summary>
    ''' <param name="name">Column name</param>
    ''' <param name="label">Column label</param>
    ''' <param name="tree">Is a tree structure or not</param>
    ''' <param name="align">Alignment of column</param>
    ''' <param name="template">Template to return data from task structures</param>
    ''' <param name="width">Width of the column</param>
    ''' <remarks></remarks>
    Public Shared Sub AddColumn(name As String, label As String, tree As Boolean, align As alignEnum, template As String, width As String)
        Dim nColumn As New dhtmlxGantt2.Column
        With nColumn
            .name = name
            .label = label
            .tree = tree
            .align = align
            .template = template
            .width = width
        End With
        Columns.Add(nColumn)
    End Sub

    ''' <summary>
    ''' Add a column to the grid
    ''' </summary>
    ''' <param name="name">Column name</param>
    ''' <param name="label">Column label</param>
    ''' <remarks></remarks>
    Public Shared Sub AddColumn(name As String, label As String)
        AddColumn(name, label, False, alignEnum.left, "", "*")
    End Sub

    ''' <summary>
    ''' Add a column to the grid
    ''' </summary>
    ''' <param name="name">Column name</param>
    ''' <param name="label">Column label</param>
    ''' <param name="tree">Is a tree structure or not </param>
    ''' <remarks></remarks>
    Public Shared Sub AddColumn(name As String, label As String, tree As Boolean)
        AddColumn(name, label, tree, alignEnum.left, "", "*")
    End Sub

    ''' <summary>
    ''' Returns the HTML for the gantt chart based on options and tasks added
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function Render() As String
        Dim clsPB As New PropertyBuilder
        sb.AppendLine("<!DOCTYPE html>")
        sb.AppendLine("<head>")
        sb.Append("<meta http-equiv=").Append(Quote).Append("Content-type").Append(Quote).Append(" content=")
        sb.Append(Quote).Append("text/html; charset=utf-8").Append(Quote).AppendLine(">")
        sb.Append("<title>").Append(Title).AppendLine("</title>")
        sb.AppendLine("</head>")
        ' add the js for the gantt chart
        sb.Append("<script ")
        clsPB.Initialize()
        clsPB.AddProperty("src", "dhtmlxgantt.js")
        clsPB.AddProperty("type", "text/javascript")
        clsPB.AddProperty("charset", "utf-8")
        sb.Append(clsPB.Render)
        sb.AppendLine("></script>")
        ' add the css for the gantt chart
        sb.Append("<link ")
        clsPB.Initialize()
        clsPB.AddProperty("rel", "stylesheet")
        Select Case Theme
            Case skinEnum.broadway
                clsPB.AddProperty("href", "dhtmlxgantt_broadway.css")
            Case skinEnum.meadow
                clsPB.AddProperty("href", "dhtmlxgantt_meadow.css")
            Case skinEnum.normal
                clsPB.AddProperty("href", "dhtmlxgantt.css")
            Case skinEnum.skyblue
                clsPB.AddProperty("href", "dhtmlxgantt_skyblue.css")
            Case skinEnum.terrance
                clsPB.AddProperty("href", "dhtmlxgantt_terrace.css")
        End Select
        clsPB.AddProperty("type", "text/css")
        clsPB.AddProperty("title", "no title")
        clsPB.AddProperty("charset", "utf-8")
        sb.Append(clsPB.Render)
        sb.AppendLine(">")
        ' include tooltips
        If ShowToolTips = True Then
            sb.Append("<script src=").Append(Quote).Append("dhtmlxgantt_tooltip.js").Append(Quote).AppendLine("></script>")
            sb.AppendLine()
        End If
        If ShowQuickInfo = True Then
            sb.Append("<script src=").Append(Quote).Append("dhtmlxgantt_quick_info.js").Append(Quote).AppendLine("></script>")
            sb.AppendLine()
        End If
        ' add body settings
        sb.Append("<style ")
        clsPB.Initialize()
        clsPB.AddProperty("type", "text/css")
        sb.Append(clsPB.Render).AppendLine(">")
        sb.AppendLine("html, body{width:100%; height:100%; padding:0px; margin:0px; overflow: hidden;}")
        ' highlight weekends
        If HighlightWeekEnds = True Then
            sb.AppendLine(".weekend{ background: #f4f7f4;}")
            sb.AppendLine(".gantt_selected .weekend{background: #f7eb91;}")
            sb.AppendLine()
        End If
        ' define circles
        sb.Append(".amber{")
        sb.Append("background:url(").Append(Quote).Append("images/amber.gif").Append(Quote).Append(") no-repeat;")
        sb.Append("width: 16px;").Append("height: 16px;").AppendLine("}")        sb.AppendLine()
        sb.Append(".green{")
        sb.Append("background:url(").Append(Quote).Append("images/green.gif").Append(Quote).Append(") no-repeat;")
        sb.Append("width: 16px;").Append("height: 16px;").AppendLine("}")        sb.AppendLine()
        sb.Append(".red{")
        sb.Append("background:url(").Append(Quote).Append("images/red.gif").Append(Quote).Append(") no-repeat;")
        sb.Append("width: 16px;").Append("height: 16px;").AppendLine("}")        sb.AppendLine()

        ' define the complete button
        sb.Append(".complete_button{")        sb.Append("margin-top: 1px;")        sb.Append("background-image:url(").Append(Quote).Append("images/v_complete.png").Append(Quote).Append(");")        sb.Append("width: 20px;").AppendLine("}")        sb.Append(".dhx_btn_set.complete_button_set{").Append("background: #ACCAAC;").Append("color: #454545;").Append("border: 1px solid #94AD94;").AppendLine("}")        sb.Append(".dhtmlx-completed{").Append("border-color: #669e60;").AppendLine("}")        sb.Append(".dhtmlx-completed div {").Append("background: #81c97a;").AppendLine("}")        ' today red line        'sb.Append(".today{").Append("border-right:2px solid #ff2424;").AppendLine("}")        sb.AppendLine(".today{ background: #FDE0E0;}")
        ' set link styles
        If ColorCodeLinks = True Then
            sb.AppendLine(".gantt_task_link.start_to_start .gantt_line_wrapper div{background-color: #dd5640;}")
            sb.AppendLine(".gantt_task_link.start_to_start:hover .gantt_line_wrapper div{box-shadow: 0 0 5px 0px #dd5640;}")
            sb.AppendLine(".gantt_task_link.start_to_start .gantt_link_arrow_right{border-left-color: #dd5640;}")
            sb.AppendLine(".gantt_task_link.finish_to_start .gantt_line_wrapper div{background-color: #7576ba;}")
            sb.AppendLine(".gantt_task_link.finish_to_start:hover .gantt_line_wrapper div{box-shadow: 0 0 5px 0px #7576ba;}")
            sb.AppendLine(".gantt_task_link.finish_to_start .gantt_link_arrow_right{border-left-color: #7576ba;}")
            sb.AppendLine(".gantt_task_link.finish_to_finish .gantt_line_wrapper div{background-color: #55d822;}")
            sb.AppendLine(".gantt_task_link.finish_to_finish:hover .gantt_line_wrapper div{box-shadow: 0 0 5px 0px #55d822;}")
            sb.AppendLine(".gantt_task_link.finish_to_finish .gantt_link_arrow_left{border-right-color: #55d822;}")
        End If
        If ShowProgressText = True Then
            sb.AppendLine(".gantt_task_progress{text-align:left;padding-left:5px;box-sizing: border-box;color:white;font-weight: normal;}")
        End If
        sb.AppendLine("</style>")
        sb.AppendLine()
        sb.AppendLine("<body>")
        sb.Append("<div ")
        clsPB.Initialize()
        clsPB.AddProperty("id", "gantt_here")
        sb.Append(clsPB.Render)
        sb.Append(" style='width:").Append(Width).Append("; height:").Append(Height).Append(";'></div").AppendLine(">")
        sb.Append("<script type=").Append(Quote).Append("text/javascript").Append(Quote).AppendLine(">")

        ' priority is given to column definitions as per locale
        ' overwrite if your column names are different
        'http://forum.dhtmlx.com/viewtopic.php?f=15&t=32883&sid=722e4e0327e669f3ed85df6fb6a1f61f
        Dim sbLocale As New StringBuilder

        ' render columns
        If Columns.Count >= 1 Then
            sb.Append("gantt.config.add_column = ").Append(Add_Column.ToString.ToLower).AppendLine(";")
            sb.Append("gantt.config.columns = [")
            For Each gColumn As dhtmlxGantt2.Column In dhtmlxGantt2.Columns
                sb.Append(gColumn.ToString).AppendLine(",")
                sbLocale.Append("gantt.locale.labels.column_").Append(gColumn.name).Append(" = ").Append(Quote).Append(gColumn.label).Append(Quote).AppendLine(";")
            Next
            sb.AppendLine("];")
        End If

        ' add locale definitions
        sb.Append(sbLocale)

        If stepBy <> 1 Then sb.Append("gantt.config.step = ").Append(stepBy.ToString).AppendLine(";")
        If Start_On_Monday = False Then sb.Append("gantt.config.start_on_monday = ").Append(Start_On_Monday.ToString.ToLower).AppendLine(";")
        If Show_Progress = True Then sb.Append("gantt.config.show_progress = ").Append(Show_Progress.ToString.ToLower).AppendLine(";")
        If Select_Task = False Then sb.Append("gantt.config.select_task = ").Append(Select_Task.ToString.ToLower).AppendLine(";")
        If Read_Only = True Then sb.Append("gantt.config.readonly = ").Append(Read_Only.ToString.ToLower).AppendLine(";")
        If Initial_Scroll = False Then sb.Append("gantt.config.initial_scroll = ").Append(Initial_Scroll.ToString.ToLower).AppendLine(";")
        If Drag_Resize = False Then sb.Append("gantt.config.drag_resize = ").Append(Drag_Resize.ToString.ToLower).AppendLine(";")
        If Drag_Move = False Then sb.Append("gantt.config.drag_move = ").Append(Drag_Move.ToString.ToLower).AppendLine(";")
        If Drag_LightBox = False Then sb.Append("gantt.config.drag_lightbox = ").Append(Drag_LightBox.ToString.ToLower).AppendLine(";")
        If Drag_Links = False Then sb.Append("gantt.config.drag_links = ").Append(Drag_Links.ToString.ToLower).AppendLine(";")
        If Details_On_Dblclick = False Then sb.Append("gantt.config.details_on_dblclick = ").Append(Details_On_Dblclick.ToString.ToLower).AppendLine(";")
        If Details_On_Create = True Then sb.Append("gantt.config.details_on_create = ").Append(Details_On_Create.ToString.ToLower).AppendLine(";")
        sb.Append("gantt.config.link_line_width = ").Append(link_line_width.ToString).AppendLine(";")
        sb.Append("gantt.config.link_arrow_size = ").Append(link_arrow_size.ToString).AppendLine(";")
        sb.Append("gantt.config.link_wrapper_width = ").Append(link_wrapper_width.ToString).AppendLine(";")
        If round_dnd_dates = False Then sb.AppendLine("gantt.config.round_dnd_dates = false;")
        If Len(start_date) > 0 Then sb.AppendLine("gantt.config.start_date = ").Append(MakeDate(start_date, +0)).AppendLine(";")
        If Len(end_date) > 0 Then sb.AppendLine("gantt.config.end_date = ").Append(MakeDate(end_date, +0)).AppendLine(";")

        ' set scale unit
        sb.Append("gantt.config.scale_unit = ").Append(Quote).Append(Scale_Unit.ToString).Append(Quote).AppendLine(";")
        sb.Append("gantt.config.date_scale = ").Append(Quote).Append(Date_Scale.ToString).Append(Quote).AppendLine(";")
        ' set date grid
        sb.Append("gantt.config.date_grid = ").Append(Quote).Append(Date_Grid).Append(Quote).AppendLine(";")
        ' set api date
        sb.Append("gantt.config.api_date = ").Append(Quote).Append(Api_Date).Append(Quote).AppendLine(";")
        ' set autofit
        sb.Append("gantt.config.autofit = ").Append(AutoFit.ToString.ToLower).AppendLine(";")
        ' render subscales
        If SubScales.Count >= 1 Then
            sb.Append("gantt.config.subscales = [")
            For Each ss As dhtmlxGantt2.SubScale In dhtmlxGantt2.SubScales
                sb.Append(ss.ToString).AppendLine(",")
            Next
            sb.AppendLine("];")
        End If
        sb.Append("gantt.config.min_column_width = ").Append(Min_Column_Width).AppendLine(";")
        If Len(Scale_Height) > 0 Then sb.Append("gantt.config.scale_height = ").Append(Scale_Height).AppendLine(";")
        If Len(Grid_Width) > 0 Then sb.Append("gantt.config.grid_width = ").Append(Grid_Width).AppendLine(";")
        sb.Append("gantt.config.row_height = ").Append(Row_Height).AppendLine(";")
        sb.Append("gantt.config.task_height = ").Append(Task_Height).AppendLine(";")
        ' add tasks
        sb.AppendLine("var tasks = {")
        sb.AppendLine("data:[")
        For Each st As dhtmlxGantt2.Task In dhtmlxGantt2.Tasks
            sb.Append(st.ToString).AppendLine(",")
        Next
        sb.AppendLine("],")
        sb.AppendLine("links:[")
        Dim lpos As Integer = 0
        For Each sl As dhtmlxGantt2.Link In dhtmlxGantt2.Links
            lpos = lpos + 1
            sb.Append("{ id:").Append(lpos.ToString).Append(sl.ToString).AppendLine(",")
        Next
        sb.AppendLine("]")
        sb.AppendLine("};")

        ' draw red line for today
        Dim cd As String = Strings.Format(Today.Date, "dd/MM/yyyy")
        sb.Append("var redtoday = ").Append(MakeDate(cd, -1)).AppendLine(";")
        sb.AppendLine("gantt.templates.task_cell_class = function(item, date){")        sb.AppendLine("if(+date == +redtoday)")        sb.Append("return ").Append(Quote).Append("today").Append(Quote).AppendLine(";")        If HighlightWeekEnds = True Then            sb.Append("if(date.getDay()==0||date.getDay()==6){")
            sb.Append("return ").Append(Quote).Append("weekend").Append(Quote).Append(";")
            sb.AppendLine("};")
        End If        sb.Append("return ").Append(Quote).Append(Quote).AppendLine(";")        sb.AppendLine("};")        '>>>
        If RightSidePattern.Count > 0 Then
            sb.AppendLine("gantt.templates.rightside_text = function(start, end, task){")
            sb.Append("return ").Append(Strings.Join(RightSidePattern.ToArray, " + ")).Append(";};")
            sb.AppendLine()
        End If
        If LeftSidePattern.Count > 0 Then
            sb.AppendLine("gantt.templates.leftside_text = function(start, end, task){")
            sb.Append("return ").Append(Strings.Join(LeftSidePattern.ToArray, " + ")).Append(";};")
            sb.AppendLine()
        End If
        If ShowProgressText = True Then
            sb.AppendLine("gantt.templates.progress_text = function(start, end, task){")
            sb.Append("return ").Append(Quote).Append("<span style='text-align:left;'>").Append(Quote).Append(" + Math.round(task.progress*100)+ ").Append(Quote)
            sb.Append("% </span>").Append(Quote).Append(";};")
        End If
        If Sortable = True Then sb.AppendLine("gantt.config.sort = true;")
        ' plot gantt
        sb.AppendLine()
        sb.Append(LightBox.Render)
        sb.AppendLine()
        sb.Append("gantt.init(").Append(Quote).Append("gantt_here").Append(Quote).AppendLine(");")
        ' colour code links
        If ColorCodeLinks = True Then
            sb.AppendLine("gantt.templates.link_class = function(link){")
            sb.AppendLine("var types = gantt.config.links;")
            sb.AppendLine("switch (link.type){")
            sb.AppendLine("case types.finish_to_start:")
            sb.Append("return ").Append(Quote).Append("finish_to_start").Append(Quote).AppendLine(";")
            sb.AppendLine("break;")
            sb.AppendLine("case types.start_to_start:")
            sb.Append("return ").Append(Quote).Append("start_to_start").Append(Quote).AppendLine(";")
            sb.AppendLine("break;")
            sb.AppendLine("case types.finish_to_finish:")
            sb.Append("return ").Append(Quote).Append("finish_to_finish").Append(Quote).AppendLine(";")
            sb.AppendLine("break;")
            sb.AppendLine("}};")
        End If
        If HideBarText = True Then
            sb.AppendLine("//defines the text inside the tak bars")
            sb.AppendLine("gantt.templates.task_text=function(start,end,task){")
            sb.Append("return ").Append(Quote).Append(Quote).AppendLine("};")        Else            If BarTextPattern.Count > 0 Then                sb.AppendLine("//defines the text inside the tak bars")
                sb.AppendLine("gantt.templates.task_text=function(start,end,task){")
                sb.Append("return ").Append(Strings.Join(BarTextPattern.ToArray, " + ")).AppendLine("};")
            End If        End If

        ' display the tasks
        sb.AppendLine("gantt.parse(tasks);")
        ' add priority
        sb.AppendLine("function myPriority(task){")
        sb.Append("if(task.priority ==1){")        sb.Append("return ").Append(Quote).Append("<div class='red'></div>").Append(Quote).AppendLine("};")        sb.Append("if(task.priority ==2){")        sb.Append("return ").Append(Quote).Append("<div class='amber'></div>").Append(Quote).AppendLine("};")        sb.Append("if(task.priority ==3){")        sb.Append("return ").Append(Quote).Append("<div class='green'></div>").Append(Quote).AppendLine("};")        sb.AppendLine("};")

        If ShowToday = True Then
            Dim td As String = Strings.Format(Today.Date, "dd/MM/yyyy")
            sb.Append("gantt.scrollTo(gantt._pos_from_date(").Append(MakeDate(td, -1)).AppendLine("));")
        End If
        ' add lightbox complete event
        sb.Append("gantt.attachEvent(").Append(Quote).Append("onLightboxButton").Append(Quote).AppendLine(", function(button_id, node, e){")
        sb.Append("if(button_id == ").Append(Quote).Append("complete_button").Append(Quote).AppendLine("){")        sb.AppendLine("var id = gantt.getState().lightbox;")
        sb.AppendLine("gantt.getTask(id).progress = 1;")        sb.AppendLine("gantt.updateTask(id)")
        sb.Append("gantt.hideLightbox();").AppendLine("}")        sb.AppendLine("});")        ' add event for saving the lightbox values        sb.Append("gantt.attachEvent(").Append(Quote).Append("onLightboxSave").Append(Quote).AppendLine(", function(id, item){")
        sb.AppendLine("if(!item.text){")        sb.Append("dhtmlx.message({type:").Append(Quote).Append("error").Append(Quote).Append(", text:").Append(Quote).Append("Enter task name please!").Append(Quote).Append("});")        sb.AppendLine("return false;")        sb.AppendLine("}")        sb.AppendLine("return true;")        sb.AppendLine("});")        sb.AppendLine()

        If ClickAbleLinks = True Then
            sb.Append("gantt.attachEvent(").Append(Quote).Append("onLinkClick").Append(Quote).AppendLine(", function(id){")
            sb.AppendLine("var link = this.getLink(id),")
            sb.AppendLine("src = this.getTask(link.source),")
            sb.AppendLine("trg = this.getTask(link.target),")
            sb.AppendLine("types = this.config.links;")
            sb.Append("var first = ").Append(Quote).Append(Quote).Append(", second = ").Append(Quote).Append(Quote).AppendLine(";")
            sb.AppendLine("switch (link.type){")
            sb.AppendLine("case types.finish_to_start:")
            sb.Append("first = ").Append(QuoteString("finish")).AppendLine(";")
            sb.Append("second = ").Append(QuoteString("start")).AppendLine(";")
            sb.AppendLine("break;")
            sb.AppendLine("case types.start_to_start:")
            sb.Append("first = ").Append(QuoteString("start")).AppendLine(";")
            sb.Append("second = ").Append(QuoteString("start")).AppendLine(";")
            sb.AppendLine("break;")
            sb.AppendLine("case types.finish_to_finish:")
            sb.Append("first = ").Append(QuoteString("finish")).AppendLine(";")
            sb.Append("second = ").Append(QuoteString("finish")).AppendLine(";")
            sb.AppendLine("break;")
            sb.AppendLine("}")

            sb.Append("dhtmlx.message(").Append(Quote).Append("Must ").Append(Quote)
            sb.Append(" + first + ").Append(Quote).Append(" <b>").Append(Quote)
            sb.Append(" + src.text + ").Append(Quote).Append("</b> to ").Append(Quote)
            sb.Append(" + second + ").Append(Quote).Append(" <b>").Append(Quote)
            sb.Append(" + trg.text + ").Append(Quote).Append("</b>").Append(Quote)
            sb.AppendLine(");")
            sb.AppendLine("});")
        End If
        sb.AppendLine("</script>")
        sb.AppendLine("</body>")
        Return sb.ToString
    End Function
End Class
